// socket.cpp
#include "socket.h"

int SOCKETAPI_InitSocket()
{
    int iRet = -1;
#ifdef WINDOWS
    int iResult;
    WORD wVersionRequested;
    WSADATA wsaData;

    wVersionRequested = MAKEWORD(2, 2);

    iResult = WSAStartup(wVersionRequested, &wsaData);
    // if ( iResult != 0 ) {
    /* Tell the user that we could not find a usable */
    /* WinSock DLL.                                  */
    // goto _Exit;
    //}

    /* Confirm that the WinSock DLL supports 2.2.*/
    /* Note that if the DLL supports versions greater    */
    /* than 2.2 in addition to 2.2, it will still return */
    /* 2.2 in wVersion since that is the version we      */
    /* requested.                                        */

    // if ( LOBYTE( wsaData.wVersion ) != 2 ||
    //	HIBYTE( wsaData.wVersion ) != 2 ) {
    //		/* Tell the user that we could not find a usable */
    //		/* WinSock DLL.                                  */
    //		WSACleanup( );
    //		goto _Exit;
    // }
#endif

    iRet = 0;
_Exit:
    return iRet;
}
int SOCKETAPI_CleanSocket()
{
    int iRet = 0;
#ifdef WINDOWS
    WSACleanup();
#endif
_Exit:
    return iRet;
}

int SOCKETAPI_CloseSocket(SOCKET iSkt)
{
#ifdef WINDOWS
    return closesocket(iSkt);
#endif
#ifdef LINUX
    return close(iSkt);
#endif
    return -1;
}

int SOCKETAPI_IsReadable(SOCKET iSkt, int iTime)
{
    fd_set rset;
    struct timeval stTmOut;

    FD_ZERO(&rset);
    FD_SET(iSkt, &rset);
    stTmOut.tv_sec  = iTime;
    stTmOut.tv_usec = 0;

    return select(iSkt + 1, &rset, NULL, NULL, &stTmOut);
}

int SOCKETAPI_IsWriteable(SOCKET iSkt, int iTime)
{
    fd_set rset;
    struct timeval stTmOut;

    FD_ZERO(&rset);
    FD_SET(iSkt, &rset);
    stTmOut.tv_sec  = iTime;
    stTmOut.tv_usec = 0;

    return select(iSkt + 1, NULL, &rset, NULL, &stTmOut);
}

int SOCKETAPI_Send(SOCKET iSkt, BYTE* data, int iLen, int iTry)
{
    int iRet = -1;
    int iResult, iTmp;
    int i, iSend = 0;

    if(data == nullptr) SET_RET_AND_GOTO_EXIT(COMMON_ERR_INV_ARGS);

    for(i = 0; i < iTry; i++)
    {
        iResult = SOCKETAPI_IsWriteable(iSkt, 1);
        if(iResult < 0) SET_RET_AND_GOTO_EXIT(COMMON_ERR_SOCKET);
        if(iResult == 0) continue;

        iResult = send(iSkt, (char*)data + iSend, iLen - iSend, 0);
        if(iResult <= 0) SET_RET_AND_GOTO_EXIT(COMMON_ERR_SOCKET);
        iSend += iResult;
        if(iSend >= iLen) break;
    }

    iRet = iSend;
_Exit:
    return iRet;
}

int SOCKETAPI_Receive(SOCKET iSkt, BYTE* pOut, int iLen, int iTry)
{
    int iRet = -1;
    int iResult, iTmp;
    int i, iRecv = 0;

    if(pOut == nullptr) SET_RET_AND_GOTO_EXIT(COMMON_ERR_INV_ARGS);

    for(i = 0; i < iTry; i++)
    {
        iResult = SOCKETAPI_IsReadable(iSkt, 1);
        if(iResult < 0) SET_RET_AND_GOTO_EXIT(COMMON_ERR_SOCKET);
        if(iResult == 0) continue;

        iResult = recv(iSkt, (char*)pOut + iRecv, iLen - iRecv, 0);
        if(iResult <= 0) SET_RET_AND_GOTO_EXIT(COMMON_ERR_SOCKET);
        iRecv += iResult;
        if(iRecv >= iLen) break;
    }

    iRet = iRecv;
_Exit:
    return iRet;
}

int SOCKETAPI_Accept(SOCKET& iSkt, OUT SOCKADDR_IN* pstAddr)
{
    int iRet = -1;
    int iResult, iTmp;
    SOCKADDR_IN stAddr;
#ifdef WINDOWS
    int iLen;
#endif
#ifdef LINUX
    U32 iLen;
#endif

    if(pstAddr == nullptr) pstAddr = &stAddr;
    bzero(pstAddr, sizeof(SOCKADDR_IN));

    iResult = accept(iSkt, (struct sockaddr*)pstAddr, &iLen);
    if(iResult == INVALID_SOCKET) iRet = COMMON_ERR_SOCKET;
    else iRet = 0;
_Exit:
    return iRet;
}
